package com.econ.powercloud.ems.timing.report;

import com.econ.powercloud.clickhouse.entity.EmsStatisticalDay;
import com.econ.powercloud.clickhouse.entity.EmsStatisticalMonth;
import com.econ.powercloud.clickhouse.entity.EmsStatisticalYear;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.aop.TimingRetryValid;
import com.econ.powercloud.ems.common.enums.DeviceType;
import com.econ.powercloud.ems.common.enums.MeterTagType;
import com.econ.powercloud.ems.common.enums.ReportIdType;
import com.econ.powercloud.ems.common.util.DateTimeUtil;
import com.econ.powercloud.ems.common.util.KeepDecimalPointUtil;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.*;
import com.econ.powercloud.ems.pojo.*;
import com.econ.powercloud.ems.service.biz.ClickHouseBizService;
import com.econ.powercloud.ems.service.common.QueryFlashDataCommonService;
import com.econ.powercloud.ems.service.dao.*;
import com.econ.powercloud.ems.util.RedisCacheUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description : 电表统计 定时任务
 * @Date : 2021/9/22 11:31
 * @Modified by : zjf
 */
@Component
public class ElectricMeterDataTask {
    //
    public static final String METER_TASK = "ems-meter-task";

    @Autowired
    private EmsDeviceDAO emsDeviceDAO;
    @Autowired
    private EmsColumnConfigDAO emsColumnConfigDAO;
    @Autowired
    private QueryFlashDataCommonService queryFlashDataCommonService;
    @Autowired
    private RedisCacheUtil redisCacheUtil;
    @Autowired
    private ClickHouseBizService clickHouseBizService;

    /**
     * 每小时统计一次
     * 电表的报表数据
     * 值 = 此时的数据 - 上一次的该数据的值
     */
    @Scheduled(cron = "0 0 */1 * * ?")
    @TimingRetryValid(lockName = METER_TASK)
    public void statisticalElectricMeterData(){
        long currentTime = System.currentTimeMillis();
        Date currentDate = new Date(currentTime);
        //获取前一个小时,当日,当月的开始时间
        long beforeOneHourTime = DateTimeUtil.getStartOfLastHour(currentDate);
        long startDayTime = DateTimeUtil.getStartOfDay(new Date(beforeOneHourTime));
        long startMonthTime = DateTimeUtil.getStartOfMonth(new Date(beforeOneHourTime));
        //获取当前时间的小时开始时间,当日,当月的开始时间
        long currentHourTime = DateTimeUtil.getStartOfHour(currentDate);
        long currentDayTime = DateTimeUtil.getStartOfDay(currentDate);
        long currentMonthTime = DateTimeUtil.getStartOfMonth(currentDate);
        //1.获取需要进行计算的电表设备有哪些
        Map<String, EmsDevice> electricMeterMap = Maps.newHashMap();
        emsDeviceDAO.getBaseMapper().selectList(null).stream().forEach(d -> {
            if (DeviceType.MULTIFUNCTION_METER.getCode().equals(d.getDeviceType()) || DeviceType.MEASURING_METER.getCode().equals(d.getDeviceType())
                    || DeviceType.DEVICE_METER.getCode().equals(d.getDeviceType())) {
                electricMeterMap.put(d.getDeviceCode(), d);
            }
        });
        if (MapUtils.isEmpty(electricMeterMap)) {
            return;
        }
        Set<EmsDevice> meterDeviceSet = Sets.newHashSet(electricMeterMap.values());
        //获取不同电表对应的列有哪些
        List<EmsColumnConfig> columnConfigList = emsColumnConfigDAO.selectAll().stream()
                .filter(d -> ReportIdType.METER.getCode().equals(d.getReportId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(columnConfigList)) {
            return;
        }
        //分成两种情况来考虑，一种是没有储能装置id的，一种是有
        Map<String, Map<Integer, List<EmsColumnConfig>>> notEquipmentColumnMap = Maps.newHashMap();
        Map<String, Map<String, Map<Integer, List<EmsColumnConfig>>>> equipmentColumnMap = Maps.newHashMap();
        //初始化这两种map
        this.getColumnByMeterDevice(columnConfigList, notEquipmentColumnMap, equipmentColumnMap);
        //查询所有的实时数据
        List<EmsColumnPidVO> columnPidVOList = this.getColumnPidConfigList(meterDeviceSet, notEquipmentColumnMap, equipmentColumnMap);
        List<EmsDeviceLocationVO> locationVOList = JacksonUtil.convertToList(columnPidVOList, EmsDeviceLocationVO.class);
        List<FlashDataVO> flashDataVOList = queryFlashDataCommonService.getFlashData(JacksonUtil.convertToList(locationVOList, QueryDataVO.class));
        //实时数据map,key:pid,value:FlashDataVO
        Map<String, FlashDataVO> flashDataMap = flashDataVOList.stream().collect(Collectors.toMap(FlashDataVO::getPid, d -> d, (p1, p2) -> p1));
        //查询上一个小时,当天开始,当月开始点号的历史数据(从redis中获取)
        Map<String, Map<String, MeterHisDataVO>> hisDataMap = redisCacheUtil.getMeterHisDataByPids(columnPidVOList);
        //存放需要更新的日电表的数据
        List<EmsStatisticalDay> statisticalDayList = Lists.newArrayList();
        //存放需要更新的月电表的数据
        List<EmsStatisticalMonth> statisticalMonthList = Lists.newArrayList();
        //存放需要更新的年电表的数据
        List<EmsStatisticalYear> statisticalYearList = Lists.newArrayList();
        //存放需要更新redis中的历史数据
        List<MeterHisDataVO> updateRedisData = Lists.newArrayList();
        //3.遍历电表设备
        for (EmsDevice emsDevice : meterDeviceSet) {
            //4.查询单个设备所需要的列有哪些
            List<EmsColumnConfig> columnConfigs = Lists.newArrayList();
            if (emsDevice.getEnergyEquipmentId() != null) {
                if (MapUtils.isNotEmpty(equipmentColumnMap.get(emsDevice.getEnergyStationId()))
                        && MapUtils.isNotEmpty(equipmentColumnMap.get(emsDevice.getEnergyStationId()).get(emsDevice.getEnergyEquipmentId()))) {
                    columnConfigs = equipmentColumnMap.get(emsDevice.getEnergyStationId()).get(emsDevice.getEnergyEquipmentId()).get(emsDevice.getDeviceType());
                }
            } else {
                if (MapUtils.isNotEmpty(notEquipmentColumnMap.get(emsDevice.getEnergyStationId()))) {
                    columnConfigs = notEquipmentColumnMap.get(emsDevice.getEnergyStationId()).get(emsDevice.getDeviceType());
                }
            }
            if (CollectionUtils.isEmpty(columnConfigs)) {
                continue;
            }
            Map<String, EmsColumnConfig> columnMap = columnConfigs.stream().collect(Collectors.toMap(EmsColumnConfig::getColumnId, d -> d, (p1, p2) -> p1));
            //遍历设备的所有的类,找到对应的点号实时数据,并进行计算
            columnMap.values().stream().forEach(d -> {
                //电表的columnId其实就是 dit
                String pid = emsDevice.getInitDeviceCode() + SysConstant.SPOT + d.getColumnId();
                //获取当前点号对应的实时数据
                FlashDataVO flashData = flashDataMap.get(pid);
                //5.计算每个小时的电表中列的数据
                MeterHisDataVO hourHisData = hisDataMap.get(MeterTagType.HOUR.getTag()) == null ? null : hisDataMap.get(MeterTagType.HOUR.getTag()).get(pid);
                if (flashData != null && hourHisData != null) {
                    Double hourVal = Double.parseDouble(flashData.getVal()) - Double.parseDouble(hourHisData.getVal());
                    EmsStatisticalDay statisticalDay = EmsStatisticalDay.builder()
                            .energyStationId(emsDevice.getEnergyStationId()).energyEquipmentId(emsDevice.getEnergyEquipmentId())
                            .deviceCode(emsDevice.getDeviceCode()).columnId(d.getColumnId()).time(beforeOneHourTime)
                            .gmtCreate(currentDate).val(KeepDecimalPointUtil.keepTwoPoint(hourVal))
                            .reportId(ReportIdType.METER.getCode())
                            .build();
                    statisticalDayList.add(statisticalDay);
                }
                //6.计算每一天的电表中列的数据
                MeterHisDataVO dayHisData = hisDataMap.get(MeterTagType.DAY.getTag()) == null ? null : hisDataMap.get(MeterTagType.DAY.getTag()).get(pid);
                if (flashData != null && dayHisData != null) {
                    Double dayVal = Double.parseDouble(flashData.getVal()) - Double.parseDouble(dayHisData.getVal());
                    EmsStatisticalMonth statisticalMonth = EmsStatisticalMonth.builder()
                            .energyStationId(emsDevice.getEnergyStationId()).energyEquipmentId(emsDevice.getEnergyEquipmentId())
                            .deviceCode(emsDevice.getDeviceCode()).columnId(d.getColumnId()).time(startDayTime)
                            .gmtCreate(currentDate).val(KeepDecimalPointUtil.keepTwoPoint(dayVal))
                            .reportId(ReportIdType.METER.getCode())
                            .build();
                    statisticalMonthList.add(statisticalMonth);
                }
                //7.计算每个月的电表中列的数据
                MeterHisDataVO monthHisData = hisDataMap.get(MeterTagType.MONTH.getTag()) == null ? null : hisDataMap.get(MeterTagType.MONTH.getTag()).get(pid);
                if (flashData != null && monthHisData != null) {
                    Double monthVal = Double.parseDouble(flashData.getVal()) - Double.parseDouble(monthHisData.getVal());
                    EmsStatisticalYear statisticalYear = EmsStatisticalYear.builder()
                            .energyStationId(emsDevice.getEnergyStationId()).energyEquipmentId(emsDevice.getEnergyEquipmentId())
                            .deviceCode(emsDevice.getDeviceCode()).columnId(d.getColumnId()).time(startMonthTime)
                            .gmtCreate(currentDate).val(KeepDecimalPointUtil.keepTwoPoint(monthVal))
                            .reportId(ReportIdType.METER.getCode())
                            .build();
                    statisticalYearList.add(statisticalYear);
                }
                if (flashData != null) {
                    //更新redis中存放的上一个小时开始数据
                    MeterHisDataVO newHourHisData = MeterHisDataVO.builder()
                            .deviceCode(emsDevice.getDeviceCode()).pid(pid)
                            .initDeviceCode(emsDevice.getInitDeviceCode())
                            .tag(MeterTagType.HOUR.getTag()).time(currentHourTime)
                            .columnId(d.getColumnId())
                            .val(flashData.getVal()).gmtCreate(currentTime)
                            .build();
                    updateRedisData.add(newHourHisData);
                    //更新redis中存放的当天开始数据
                    if (startDayTime != currentDayTime || dayHisData == null) {
                        MeterHisDataVO newDayHisData = MeterHisDataVO.builder()
                                .deviceCode(emsDevice.getDeviceCode()).pid(pid)
                                .initDeviceCode(emsDevice.getInitDeviceCode())
                                .tag(MeterTagType.DAY.getTag()).time(currentDayTime)
                                .columnId(d.getColumnId())
                                .val(flashData.getVal()).gmtCreate(currentTime)
                                .build();
                        updateRedisData.add(newDayHisData);
                    }
                    //更新redis中存放的当月开始数据
                    if (startMonthTime != currentMonthTime || monthHisData == null) {
                        MeterHisDataVO newMonthHisData = MeterHisDataVO.builder()
                                .deviceCode(emsDevice.getDeviceCode()).pid(pid)
                                .initDeviceCode(emsDevice.getInitDeviceCode())
                                .tag(MeterTagType.MONTH.getTag()).time(currentMonthTime)
                                .columnId(d.getColumnId())
                                .val(flashData.getVal()).gmtCreate(currentTime)
                                .build();
                        updateRedisData.add(newMonthHisData);
                    }
                }
            });
        }
        //数据库更新操作
        //        emsStatisticalDayDAO.deleteByTimeAndReportId(beforeOneHourTime, ReportIdType.METER.getCode());
        clickHouseBizService.deleteDayDataByTime(beforeOneHourTime, ReportIdType.METER.getCode());
        if (CollectionUtils.isNotEmpty(statisticalDayList)) {
            //            emsStatisticalDayDAO.insertBatch(statisticalDayList);
            clickHouseBizService.emsStatisticalDayListDataIntoClickHouse(statisticalDayList);
        }
        //        emsStatisticalMonthDAO.deleteByTimeAndReportId(startDayTime, ReportIdType.METER.getCode());
        clickHouseBizService.deleteMonthDataByTime(startDayTime, ReportIdType.METER.getCode());
        if (CollectionUtils.isNotEmpty(statisticalMonthList)) {
            //            emsStatisticalMonthDAO.insertBatch(statisticalMonthList);
            clickHouseBizService.emsStatisticalMonthListDataIntoClickHouse(statisticalMonthList);
        }
        //        emsStatisticalYearDAO.deleteByTimeAndReportId(startMonthTime, ReportIdType.METER.getCode());
        clickHouseBizService.deleteYearDataByTime(startMonthTime, ReportIdType.METER.getCode());
        if (CollectionUtils.isNotEmpty(statisticalYearList)) {
            //            emsStatisticalYearDAO.insertBatch(statisticalYearList);
            clickHouseBizService.emsStatisticalYearListDataIntoClickHouse(statisticalYearList);
        }
        //更新redis操作
        if (CollectionUtils.isNotEmpty(updateRedisData)) {
            redisCacheUtil.updateHisRedisDataByMeterHisDataVO(updateRedisData);
        }
    }

    /**
     * 获取电表需要展示的列有哪些
     */
    public void getColumnByMeterDevice(List<EmsColumnConfig> columnConfigList,
                                       Map<String,Map<Integer,List<EmsColumnConfig>>> notEquipmentColumnMap,
                                       Map<String,Map<String,Map<Integer,List<EmsColumnConfig>>>> equipmentColumnMap){
        if(CollectionUtils.isEmpty(columnConfigList)){
            return ;
        }
        //筛选出储能装置不为空的数据,在进行groupby
        Map<String,List<EmsColumnConfig>> equipmentStationMap = columnConfigList.stream().filter(d-> d.getEnergyEquipmentId()!=null).collect(Collectors.groupingBy(d->d.getEnergyStationId()));
        equipmentStationMap.forEach((k,v)->{
            Map<String,Map<Integer,List<EmsColumnConfig>>> map = Maps.newHashMap();
            Map<String,List<EmsColumnConfig>> equipmentMap = v.stream().collect(Collectors.groupingBy(d->d.getEnergyEquipmentId()));
            equipmentMap.forEach((k1,v1)->{
                Map<Integer,List<EmsColumnConfig>> deviceTypeMap = v1.stream().collect(Collectors.groupingBy(d->d.getDeviceType()));
                map.put(k1,deviceTypeMap);
            });
            equipmentColumnMap.put(k,map);
        });
        //筛选出储能装置为空的数,在进行groupby
        Map<String,List<EmsColumnConfig>> notEquipmentStationMap = columnConfigList.stream().filter(d-> d.getEnergyEquipmentId()==null).collect(Collectors.groupingBy(d->d.getEnergyStationId()));
        notEquipmentStationMap.forEach((k,v)->{
            Map<Integer,List<EmsColumnConfig>> map = v.stream().collect(Collectors.groupingBy(d->d.getDeviceType()));
            notEquipmentColumnMap.put(k,map);
        });
    }

    /**
     * 根据电表设备集合获取需要查询的实时数据
     */
    public List<EmsColumnPidVO> getColumnPidConfigList(Set<EmsDevice> emsDeviceSet, Map<String,Map<Integer,List<EmsColumnConfig>>> notEquipmentColumnMap,
                                                       Map<String,Map<String,Map<Integer,List<EmsColumnConfig>>>> equipmentColumnMap){
        List<EmsColumnPidVO> emsColumnPidVOList = Lists.newArrayList();
        if(CollectionUtils.isEmpty(emsDeviceSet)){
            return emsColumnPidVOList;
        }
        //拼接数据
        emsDeviceSet.stream().forEach(d->{
            List<EmsColumnConfig> emsColumnConfigs = Lists.newArrayList();
            if(d.getEnergyEquipmentId() != null){
                if(MapUtils.isNotEmpty(equipmentColumnMap.get(d.getEnergyStationId()))
                    && MapUtils.isNotEmpty(equipmentColumnMap.get(d.getEnergyStationId()).get(d.getEnergyEquipmentId()))){
                    emsColumnConfigs.addAll(equipmentColumnMap.get(d.getEnergyStationId()).get(d.getEnergyEquipmentId()).get(d.getDeviceType()));
                }
            }else{
                if(MapUtils.isNotEmpty(notEquipmentColumnMap.get(d.getEnergyStationId()))){
                    emsColumnConfigs.addAll(notEquipmentColumnMap.get(d.getEnergyStationId()).get(d.getDeviceType()));
                }
            }
            emsColumnConfigs.stream().forEach(f->{
                emsColumnPidVOList.add(EmsColumnPidVO.builder()
                        .columnId(f.getColumnId()).deviceCode(d.getDeviceCode())
                        .initDeviceCode(d.getInitDeviceCode())
                        .dit(f.getColumnId())
                        .pid(d.getInitDeviceCode() + SysConstant.SPOT + f.getColumnId())
                        .build());
            });
        });
        return emsColumnPidVOList;
    }

}
